package com.epoint.netty.chat.client;

import com.epoint.netty.chat.message.*;
import com.epoint.netty.chat.protocol.MessageCodecSharable;
import com.epoint.netty.chat.protocol.ProcotolFrameDecoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

@Slf4j
public class ChatClient {
    public static void main(String[] args) {
        NioEventLoopGroup group = new NioEventLoopGroup();
        LoggingHandler LOGGING_HANDLER = new LoggingHandler(LogLevel.DEBUG);
        MessageCodecSharable MESSAGE_CODEC = new MessageCodecSharable();

        AtomicBoolean isLogin = new AtomicBoolean(false);
        CountDownLatch countDownLatch = new CountDownLatch(1);

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.group(group);
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    // 设定了客户端读写的心跳时间，比如：3秒之内没有服务器发消息了
                    ch.pipeline().addLast(new IdleStateHandler(0, 3, 0, TimeUnit.SECONDS));
                    // 是配合IdleStateHandler一起使用，如果5秒内没传数据过来 就触发一个方法
                    ch.pipeline().addLast(new ChannelDuplexHandler(){
                        @Override
                        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                            IdleStateEvent event = (IdleStateEvent) evt;
                            // 触发了读空闲事件
                            if (event.state() == IdleState.WRITER_IDLE) {
                                // 因为5秒之内不给服务器发消息 服务器就把我们断开了，所以我们3秒给他发给消息（我们有个雅称：心跳包）
                                PingMessage pingMessage = new PingMessage();
                                ctx.channel().writeAndFlush(pingMessage);
                            }
                        }
                    });
                    ch.pipeline().addLast(new ProcotolFrameDecoder());
                    ch.pipeline().addLast(LOGGING_HANDLER);
                    ch.pipeline().addLast(MESSAGE_CODEC);
                    ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
                        @Override
                        public void channelActive(ChannelHandlerContext ctx) throws Exception {
                            // 此处开一个线程，是因为主流程会在channelRead 进行while(true) + Scanner
                            new Thread(()->{
                                // 与服务器链接成功后
                                System.out.println("请输入账号：");
                                Scanner scanner = new Scanner(System.in);
                                String username = scanner.nextLine();

                                System.out.println("请输入密码：");
                                String password = scanner.nextLine();

                                // 传给服务器
                                LoginRequestMessage loginRequestMessage = new LoginRequestMessage(username, password, username);
                                ctx.channel().writeAndFlush(loginRequestMessage);

                                System.out.println("等待服务器验证！！！！");

                                try {
                                    countDownLatch.await();
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }

                                while (true){
                                    // 如果登录成功
                                    if(isLogin.get()){
                                        System.out.println("==================================");
                                        System.out.println("send [username] [content]");
                                        System.out.println("gsend [group name] [content]");
                                        System.out.println("gcreate [group name] [m1,m2,m3...]");
                                        System.out.println("gmembers [group name]");
                                        System.out.println("gjoin [group name]");
                                        System.out.println("gquit [group name]");
                                        System.out.println("quit");
                                        System.out.println("==================================");

                                        String cmd = scanner.nextLine();

                                        String[] s = cmd.split(" ");

                                        switch (s[0]){
                                            case "send":
                                                ctx.writeAndFlush(new ChatRequestMessage(username, s[1], s[2]));
                                                break;
                                            case "gsend":
                                                ctx.writeAndFlush(new GroupChatRequestMessage(username, s[1], s[2]));
                                                break;
                                            case "gcreate":
                                                Set<String> set = new HashSet<>(Arrays.asList(s[2].split(",")));
                                                set.add(username); // 加入自己
                                                ctx.writeAndFlush(new GroupCreateRequestMessage(s[1], set));
                                                break;
                                            case "gmembers":
                                                ctx.writeAndFlush(new GroupMembersRequestMessage(s[1]));
                                                break;
                                            case "gjoin":
                                                ctx.writeAndFlush(new GroupJoinRequestMessage(username, s[1]));
                                                break;
                                            case "gquit":
                                                ctx.writeAndFlush(new GroupQuitRequestMessage(username, s[1]));
                                                break;
                                            case "quit":
                                                ctx.channel().close();
                                                return;
                                        }
                                        ctx.channel().writeAndFlush(cmd);
                                    }
                                    else{
                                        System.out.println("密码不正确，对不起");
                                        ctx.channel().close();
                                        return;
                                    }
                                }

                            }).start();
                        }

                        // 这边用来接收消息，然后异步的通知channelActive的线程
                        @Override
                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                            // 登录消息
                            if(msg instanceof LoginResponseMessage){
                                LoginResponseMessage logResult = (LoginResponseMessage) msg;
                                // 如果不成功
                                if(logResult.isSuccess()){
                                    // 打开开关
                                    isLogin.set(true);
                                }

                                // active流程向下送
                                countDownLatch.countDown();
                            }
                            else if(msg instanceof ChatResponseMessage){
                                ChatResponseMessage message = (ChatResponseMessage) msg;
                                String from = message.getFrom();
                                String content = message.getContent();

                                System.out.println("用户（"+ from +"）：" + content);
                            }
                            else if(msg instanceof GroupCreateResponseMessage){
                                GroupCreateResponseMessage message = (GroupCreateResponseMessage) msg;
                                System.out.println(message.getReason());
                            }
                        }

                        // 在连接断开时触发
                        @Override
                        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                            log.debug("连接已经断开，按任意键退出..");
                        }

                        // 在出现异常时触发
                        @Override
                        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                            log.debug("连接已经断开，按任意键退出..{}", cause.getMessage());
                        }

                    });

                }
            });
            Channel channel = bootstrap.connect("localhost", 8080).sync().channel();
            channel.closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }
    }

    private static void login(ChannelHandlerContext ctx) {
        // 与服务器链接成功后
        System.out.println("请输入账号：");
        Scanner scanner = new Scanner(System.in);
        String username = scanner.nextLine();

        System.out.println("请输入密码：");
        String password = scanner.nextLine();

        // 传给服务器
        LoginRequestMessage loginRequestMessage = new LoginRequestMessage(username, password, username);
        ctx.channel().writeAndFlush(loginRequestMessage);
    }
}
